import { Request, Response, NextFunction } from 'express';
import { verifyAccessToken } from '../config/jwt';
import { createError } from './errorHandler';
import { UserService } from '../services/UserService';
import { JwtPayload } from '../types';

// 扩展Request接口以包含用户信息
declare global {
  namespace Express {
    interface Request {
      user?: {
        id: number;
        email: string;
        role: string;
        firstName?: string;
        lastName?: string;
        provider: 'local' | 'google' | 'github';
      };
    }
  }
}

/**
 * JWT认证中间件
 * 验证Bearer token并将用户信息添加到request对象
 */
export const authenticate = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
  try {
    // 从请求头获取token
    const authHeader = req.headers.authorization;
    
    if (!authHeader) {
      throw createError('需要认证令牌', 401);
    }
    
    if (!authHeader.startsWith('Bearer ')) {
      throw createError('认证令牌格式无效，应为: Bearer <token>', 401);
    }
    
    const token = authHeader.substring(7); // 移除 'Bearer ' 前缀
    
    if (!token) {
      throw createError('认证令牌不能为空', 401);
    }
    
    // 验证token
    let payload: JwtPayload;
    try {
      payload = verifyAccessToken(token);
    } catch (error: any) {
      if (error.name === 'TokenExpiredError') {
        throw createError('认证令牌已过期', 401);
      } else if (error.name === 'JsonWebTokenError') {
        throw createError('无效的认证令牌', 401);
      } else {
        throw createError('令牌验证失败', 401);
      }
    }
    
    // 从数据库获取用户信息
    const userService = new UserService();
    const user = await userService.findUserById(payload.userId);
    
    if (!user) {
      throw createError('用户不存在', 401);
    }
    
    if (!user.isActive) {
      throw createError('用户账户已被禁用', 401);
    }
    
    // 将用户信息添加到request对象
    req.user = {
      id: user.id,
      email: user.email,
      role: payload.role,
      firstName: user.firstName,
      lastName: user.lastName,
      provider: user.provider,
    };
    
    next();
  } catch (error) {
    next(error);
  }
};

/**
 * 可选的认证中间件
 * 如果有token则验证，没有则继续
 */
export const optionalAuth = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
  try {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return next();
    }
    
    const token = authHeader.substring(7);
    
    if (!token) {
      return next();
    }
    
    try {
      const payload = verifyAccessToken(token);
      const userService = new UserService();
      const user = await userService.findUserById(payload.userId);
      
      if (user && user.isActive) {
        req.user = {
          id: user.id,
          email: user.email,
          role: payload.role,
          firstName: user.firstName,
          lastName: user.lastName,
          provider: user.provider,
        };
      }
    } catch (error) {
      // 忽略token验证错误，继续处理请求
      console.warn('可选认证失败，继续处理请求:', error);
    }
    
    next();
  } catch (error) {
    next(error);
  }
};

/**
 * 角色权限中间件
 * @param roles 允许的角色列表
 */
export const authorize = (roles: string[]) => {
  return (req: Request, res: Response, next: NextFunction): void => {
    if (!req.user) {
      throw createError('需要认证', 401);
    }
    
    if (!roles.includes(req.user.role)) {
      throw createError('权限不足', 403);
    }
    
    next();
  };
};

/**
 * 管理员权限中间件
 */
export const requireAdmin = authorize(['admin']);

/**
 * 用户权限中间件（用户或管理员）
 */
export const requireUser = authorize(['user', 'admin']);

/**
 * 检查用户是否为资源所有者或管理员
 * @param getUserId 获取资源所属用户ID的函数
 */
export const requireOwnerOrAdmin = (getUserId: (req: Request) => number | string) => {
  return (req: Request, res: Response, next: NextFunction): void => {
    if (!req.user) {
      throw createError('需要认证', 401);
    }
    
    const resourceUserId = Number(getUserId(req));
    const requestUserId = req.user.id;
    const userRole = req.user.role;
    
    if (userRole === 'admin' || requestUserId === resourceUserId) {
      next();
    } else {
      throw createError('只能访问自己的资源', 403);
    }
  };
};